package healthylife.top.sync;

import cn.hutool.core.date.SystemClock;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;

@SuppressWarnings("unused")
@Slf4j
public class FileSyncHelper {

    //    private static final FileSyncService fileSyncService = FileSyncServiceFactory.ins(FileSyncServiceFactory.FileServerType.LOCAL);
    private static final FileSyncService fileSyncService = FileSyncServiceFactory.ins(FileSyncServiceFactory.FileServerType.MINIO);
    private static final ExecutorService threadPool = Executors.newFixedThreadPool(10);
    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    // 同步状态和统计信息
    private static final ReentrantLock lock = new ReentrantLock();
    private static List<String> totalFiles = new ArrayList<>();
    private static Integer totalOperator = 0;
    private static Integer okFinishedOperator = 0;
    private static Integer failedFinishedOperator = 0;
    private static final List<String> newFilesOk = new ArrayList<>();
    private static final List<String> newFilesFailed = new ArrayList<>();
    private static final List<String> updatedFilesOk = new ArrayList<>();
    private static final List<String> updatedFilesFailed = new ArrayList<>();
    private static final List<String> deletedFilesOk = new ArrayList<>();
    private static final List<String> deletedFilesFailed = new ArrayList<>();
    private static final String startTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyddMMHHmmss"));
    private static final Map<String, FileInfo> sourceFiles = getSourceFiles();
    private static final Map<String, FileInfo> targetFiles = getTargetFiles();


    public static void printProgress() {

        // test 模拟 查看进度
        scheduler.scheduleAtFixedRate(() -> {
            String syncProgress = getSyncProgress();
            log.info("当前进度: " + syncProgress);
            double progress = totalOperator == 0 ? 0.00 : (okFinishedOperator + failedFinishedOperator) * 100.00 / totalOperator;
            String formattedProgress = String.format("%.2f%%", progress);
            int progressPercentage = (int) progress;
            ProcessBar.updateProgress(progressPercentage);
            System.out.print("\n");
        }, 0, 5, TimeUnit.SECONDS);
    }

    public static void syncFiles() {
        long now = SystemClock.now();
        log.info("任务同步开始,同步文件目录:{}-->{}", fileSyncService.getSource(), fileSyncService.getTarget());
        printProgress();
        try {
            totalFiles = sourceFiles.values().stream().map(FileInfo::getFilePath).collect(Collectors.toList());
            initLog();
            // 比较并同步文件
            List<Future<?>> futures = new ArrayList<>();
            for (Map.Entry<String, FileInfo> entry : sourceFiles.entrySet()) {
                String filePath = entry.getKey();
                //source 有 而 target 没有
                if (!targetFiles.containsKey(filePath)) {
                    // save or update
                    futures.add(submitTransferTask(filePath));
                }
            }

            for (Map.Entry<String, FileInfo> entry : targetFiles.entrySet()) {
                String filePath = entry.getKey();
                if (!sourceFiles.containsKey(filePath)) {
                    // 删除文件
                    futures.add(submitDeleteTask(filePath));
                }
            }

            // 等待所有任务完成
            for (Future<?> future : futures) {
                future.get();
            }
            writeConfigFile();
            long consume = SystemClock.now() - now;
            endLog(consume);
            log.info("任务同步结束:耗时:{}ms,{},日志文件:{}", consume, getSyncProgress(), fileSyncService.getLog(startTime));
        } catch (Exception e) {
            log.error("同步失败:", e);
        } finally {
            shutdownExecutors();
        }
    }


    private static void initLog() throws IOException {
        String syncProgress = "同步任务开始" + System.lineSeparator() + "总文件数:" + totalFiles.size();
        String fileName = fileSyncService.getLog(startTime);
        Path path = Paths.get(fileName);
        if (!path.toFile().exists()) {
            Path parent = path.getParent();
            if (!parent.toFile().exists()) {
                Files.createDirectories(parent);
            }
            Files.createFile(path);
            Files.write(path, syncProgress.getBytes(Charset.defaultCharset()));
        }
    }

    private static void updateLog(String filePath, String operation, String state) {
        try {
            String fileName = fileSyncService.getLog(startTime);
            Path path = Paths.get(fileName);
            // 如果文件已存在，则换行追加内容
            String contentToAppend = System.lineSeparator() + operation + filePath + "\t" + state;
            Files.write(path, contentToAppend.getBytes(Charset.defaultCharset()), StandardOpenOption.APPEND);
            log.info("记录日志");
        } catch (Exception e) {
            log.error("日志写入失败：", e);
        }
    }

    private static void endLog(long consume) throws IOException {
        String syncProgress = System.lineSeparator() + "同步任务结束,耗时:" + consume + "ms";
        String fileName = fileSyncService.getLog(startTime);
        Path path = Paths.get(fileName);
        Files.write(path, syncProgress.getBytes(Charset.defaultCharset()), StandardOpenOption.APPEND);
    }

    private static Map<String, FileInfo> getSourceFiles() {
        List<FileInfo> fileInfos = fileSyncService.sourceFiles();
        return fileInfos.stream().collect(Collectors.toMap(FileInfo::getFilePath, Function.identity()));
    }

    private static Map<String, FileInfo> getTargetFiles() {
        List<FileInfo> fileInfos = fileSyncService.targetFiles();
        return fileInfos.stream().collect(Collectors.toMap(FileInfo::getFilePath, Function.identity()));
    }

    private static Future<?> submitTransferTask(String filePath) {
        return threadPool.submit(() -> {
            try {
                synchronized (filePath) {
                    totalOperator += 1;
                }
                log.info("文件同步中：{}", filePath);
                int transfer = transfer(filePath, filePath);
                if (transfer == 0) {
                    synchronized (newFilesOk) {
                        newFilesOk.add(filePath);
                        okFinishedOperator += 1;
                    }
                    updateLog(filePath, "新增:", "成功");
                } else if (transfer == 1) {
                    synchronized (updatedFilesOk) {
                        updatedFilesOk.add(filePath);
                        okFinishedOperator += 1;
                    }
                    updateLog(filePath, "更新:", "成功\n" + getSyncProgress());
                }
                log.info("文件transfer:{} 成功 ", filePath);
            } catch (Exception e) {
                log.error("文件transfer失败:{},错误：{}", filePath, e.getMessage());
                handleTransferFailure(filePath, e);
            }
        });
    }

    private static Future<?> submitDeleteTask(String filePath) {
        return threadPool.submit(() -> {
            try {
                synchronized (filePath) {
                    totalOperator += 1;
                }
                log.info("准备删除文件: " + filePath);
                deleteLocalFile(filePath);
                synchronized (deletedFilesOk) {
                    deletedFilesOk.add(filePath);
                    okFinishedOperator += 1;
                }
                updateLog(filePath, "删除:", "成功");
                log.info("已删除: " + filePath);
            } catch (IOException e) {
                log.error("删除失败: {}, 错误: {}", filePath, e.getMessage());
                synchronized (deletedFilesFailed) {
                    deletedFilesFailed.add(filePath);
                    failedFinishedOperator += 1;
                }
                updateLog(filePath, "删除:", "失败:" + e.getMessage());
            }
        });
    }

    private static void handleTransferFailure(String filePath, Exception e) {
        if (e.getMessage() != null && e.getMessage().contains("code:0")) {
            synchronized (newFilesFailed) {
                newFilesFailed.add(filePath);
                failedFinishedOperator += 1;
            }
            updateLog(filePath, "新增:", "失败" + e.getMessage());
        } else if (e.getMessage() != null && e.getMessage().contains("code:1")) {
            synchronized (updatedFilesFailed) {
                updatedFilesFailed.add(filePath);
                failedFinishedOperator += 1;
            }
            updateLog(filePath, "更新:", "失败" + e.getMessage());
        }
    }

    private static void writeConfigFile() {
        ThreadUtil.execAsync(() -> {
            try {
                Path path = Paths.get(fileSyncService.getConf());
                IoUtil.write(Files.newOutputStream(path), true, JSONUtil.toJsonStr(new ArrayList<>(sourceFiles.values())).getBytes(Charset.defaultCharset()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    private static int transfer(String filePath, String target) {
        return fileSyncService.transfer(filePath, target);
    }

    private static void deleteLocalFile(String filePath) throws IOException {
        fileSyncService.deleteFile(filePath);
    }


    private static String getSyncProgress() {
        // 实时查询同步进度
        double progress = totalOperator == 0 ? 0.00 : (okFinishedOperator + failedFinishedOperator) * 100.00 / totalOperator;
        String formattedProgress = String.format("%.2f%%", progress);
        return String.format("总文件数:%d,总操作数:%d(成功:%d 失败:%d),进度:%s, 新增: %d(总操作数) %d(成功) %d(失败), 更新: %d(总操作数) %d(成功) %d(失败), 删除: %d(总操作数) %d(成功) %d(失败)",
                totalFiles.size(),
                totalOperator,
                okFinishedOperator,
                failedFinishedOperator,
                formattedProgress,
                newFilesOk.size() + newFilesFailed.size(),
                newFilesOk.size(),
                newFilesFailed.size(),
                updatedFilesOk.size() + updatedFilesFailed.size(),
                updatedFilesOk.size(),
                updatedFilesFailed.size(),
                deletedFilesOk.size() + deletedFilesFailed.size(),
                deletedFilesOk.size(),
                deletedFilesFailed.size()
        );
    }

    private static void shutdownExecutors() {
        if (!threadPool.isShutdown()) {
            threadPool.shutdown();
        }
        if (!scheduler.isShutdown()) {
            scheduler.shutdown();
        }
    }

}